
/* This file contains portions of the GPL libmd3 examples */

#include <iostream>
#include <string>

#include "GLSL.hh"

#include <ode/ode.h>

#include <GL/gl.h>
#include <GL/glu.h>

#include "Renderable.hh"

//#define M_PI 3.14141592f

#define scaleFactor 0.015625f

Renderable::Renderable(){
  mName = "Undefined";
  mX = 0;
  mY = 0;
  mZ = 0;
}

Renderable::Renderable(const std::string& name, double x, double y, double z, const char* md3file){
  mX = x;
  mY = y;
  mZ = z;
  mName = name;

  nextFrame = 0;
  
  mMd3model = libmd3_file_load(md3file);
  if (mMd3model == NULL) {
    std::cerr << md3file << " not found!" << std::endl;
    std::cout << "Aborting execution" << std::endl;
    exit(1);
 }

  std::cout << "# Frames: " << mMd3model->header->frame_count << endl;

  int i;

  libmd3_mesh* meshp;
  meshp = mMd3model->meshes;


  for (i = 0; i < mMd3model->header->mesh_count; ++i, ++meshp) {
      unsigned char *c;
      
      for (c = mMd3model->meshes[i].skins[0].name; *c; c++)
	if (*c == '\\')
	  *c = '/';
      
      libmd3_unpack_normals(&mMd3model->meshes[i]);
      
      
  }


  
  //LoadAllTextures(); 

}


Renderable&
Renderable::operator=(const Renderable& r){
  mName = r.mName;
  mX = r.mX;
  mY = r.mY;
  mZ = r.mZ;
  return *this;
}

Renderable::~Renderable(){
  libmd3_file_free(mMd3model);
}

void
Renderable::SetName(std::string& name){
  mName = name;
}

void
Renderable::Render(){
  

  glPushMatrix();

  glScalef(scaleFactor,scaleFactor,scaleFactor);

  draw_md3_file();
    
  glPopMatrix();

}

const std::string
Renderable::GetName(){
  return mName;
}

double
Renderable::GetPosX(){
  return mX;
}

double
Renderable::GetPosY(){
  return mY;
}

double
Renderable::GetPosZ(){
  return mZ;
}

Point
Renderable::GetPos(){
  Point p;

  p.x = mX;
  p.y = mY;
  p.z = mZ;
  
  return p;

}

void
Renderable::SetPos(double x, double y, double z){
  mX=x; mY=y; mZ=z;
}


SDL_Surface* 
Renderable::LoadBMP(const char *filename)
{
    Uint8 *rowhi, *rowlo;
    Uint8 *tmpbuf, tmpch;
    SDL_Surface *image;
    int i, j;

    image = SDL_LoadBMP(filename);
    if ( image == NULL ) {
             fprintf(stderr, "Unable to load %s: %s\n", filename, SDL_GetError());
        return(NULL);
    }

    /* GL surfaces are upsidedown and RGB, not BGR :-) */
    tmpbuf = (Uint8 *)malloc(image->pitch);
    if ( tmpbuf == NULL ) {
        fprintf(stderr, "Out of memory\n");
        return(NULL);
    }
    rowhi = (Uint8 *)image->pixels;
    rowlo = rowhi + (image->h * image->pitch) - image->pitch;
    for ( i=0; i<image->h/2; ++i ) {
        for ( j=0; j<image->w; ++j ) {
            tmpch = rowhi[j*3];
            rowhi[j*3] = rowhi[j*3+2];
            rowhi[j*3+2] = tmpch;
            tmpch = rowlo[j*3];
            rowlo[j*3] = rowlo[j*3+2];
            rowlo[j*3+2] = tmpch;
        }
        memcpy(tmpbuf, rowhi, image->pitch);
        memcpy(rowhi, rowlo, image->pitch);
        memcpy(rowlo, tmpbuf, image->pitch);
        rowhi += image->pitch;
        rowlo -= image->pitch;
    }
    free(tmpbuf);
    return(image);
}

/* Load Bitmaps And Convert To Textures */
GLuint 
Renderable::LoadGLTexture(const char * filename)
{       
    /* Load Texture */
    SDL_Surface *image1;
    GLuint texture;
    
    image1 = LoadBMP(filename);
    if (!image1) {
        return 0;
    }

    /* Create Texture */
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture); /* 2d texture (x and y size) */

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->w, image1->h, 0, GL_RGB,
                 GL_UNSIGNED_BYTE, image1->pixels);
    return texture;
}



void 
Renderable::draw_one_mesh(libmd3_mesh* mesh)
{
  if (mesh->mesh_header->skin_count != 0) {
    if (mesh->user.u == 0) {
      mesh->user.u = LoadGLTexture((char*) mesh->skins[0].name);
    }
  }
  
    if (mesh->user.u != 0) {
        glEnable(GL_TEXTURE_2D);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glBindTexture(GL_TEXTURE_2D, mesh->user.u);
    }


    glEnableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(3, GL_SHORT, 0, mesh->vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, mesh->texcoords);
    glNormalPointer(GL_FLOAT, 0, mesh->normals);
    glDrawElements(GL_TRIANGLES, mesh->mesh_header->triangle_count * 3,
                   GL_UNSIGNED_INT, mesh->triangles);

    glDisableClientState(GL_NORMAL_ARRAY);
   
    if (mesh->user.u != 0) {
        glDisable(GL_TEXTURE_2D);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }
}


void 
Renderable::draw_md3_file()
{
  int i;
  libmd3_mesh* meshp;


  if (mMd3model->header->mesh_count == 0) {
    printf("[No meshes in file]\n");
    return;
  }

  meshp = mMd3model->meshes;

  for(i = 0; i < mMd3model->header->mesh_count; i++, meshp++) {
    draw_one_mesh(meshp);

  }

  
}

libmd3_file* 
Renderable::GetModel(){
  return mMd3model;
}

void 
Renderable::LoadAllTextures(){
  
  int i;
  libmd3_mesh* meshp;

  meshp = mMd3model->meshes;
  
  cout << "Loading textures" << endl;
  
  for(i = 0; i < mMd3model->header->mesh_count; ++i, ++meshp) {
    if (meshp->mesh_header->skin_count != 0) {
      if (meshp->user.u == 0) {
	meshp->user.u = LoadGLTexture((char*) meshp->skins[0].name);
	cout << meshp->skins[0].name << " loaded - " <<  meshp->user.u << endl;
      }
    }  
  }
  
}
